package parserTable;

import java.io.FileNotFoundException;
import java.util.ArrayList;

import java.util.Stack;

import lexer.*;

public class ParserTable {
		
	ArrayList<Token> listaTokens;
	Stack<Object> pilha;	
	NTerminais NT_S, NT_SD, NT_SD1, NT_SD2, NT_DECL, NT_SA, NT_ATR, NT_ATR1, NT_EXP, NT_EXP1, NT_OP, NT_OP1, NT_SA1,J,TIPO;
	
	public ParserTable(Lexer l) throws FileNotFoundException {
		listaTokens = new ArrayList<Token>();
		listaTokens = l.getListAllTokens();	
		pilha = new Stack<Object>();	
        this.startTable();
	}
	
	public void startTable()
	{
		Producao p1, p2, p3, p4, p5;
		//start
		NT_S = new NTerminais("S");
		Grammar g1 = new Grammar("@");
		p1 = new Producao(true, "DECL");
		p2 = new Producao(true, "SD");
		p3 = new Producao(true, "SA");
		g1.adiciona(p1);
		g1.adiciona(p2);
		g1.adiciona(p3);
		NT_S.adiciona(g1);
                
                // J:
                J = new NTerminais("J");
                Grammar g12 = new Grammar("@");
                p3 = new Producao (false,"@");
                p4 = new Producao (false,"id");
                g12.adiciona(p3);
                g12.adiciona(p4);
                J.adiciona(g12);
                g12 = new Grammar("literal");
                p3 = new Producao(false,"literal");
                g12.adiciona(p3);
                J.adiciona(g12);
                
		//TIPO.
                TIPO = new NTerminais("TIPO");
                 g12 = new Grammar("INTEGER");
                 p1 = new Producao(false,"INTEGER");
                 g12.adiciona(p1);
                 TIPO.adiciona(g12);
                 
                 g12 = new Grammar("FLOAT");
                 p1 = new Producao(false,"FLOAT");
                 g12.adiciona(p1);
                 TIPO.adiciona(g12);
                 
                 
                 g12 = new Grammar("STRING");
                 p1 = new Producao(false,"STRING");
                 g12.adiciona(p1);
                 TIPO.adiciona(g12);
                 
                 g12 = new Grammar("BOOLEAN");
                 p1 = new Producao(false,"BOOLEAN");
                 g12.adiciona(p1);
                 TIPO.adiciona(g12);
                 
                
		//DECL
		NT_DECL = new NTerminais("DECL");
		Grammar g2 = new Grammar("@"); //@
		p1 = new Producao(false, "@");
		p2 = new Producao(false, "id");
		p3 = new Producao(false, ":");
		p4 = new Producao(true, "TIPO");
		p5 = new Producao(false, ".new");
		g2.adiciona(p1);
		g2.adiciona(p2);
		g2.adiciona(p3);
		g2.adiciona(p4);
		g2.adiciona(p5);
		NT_DECL.adiciona(g2);
		
		g2 = new Grammar("\n");
                Producao p0 = new Producao(false,"\n");
		p1 = new Producao(false, "@");
		p2 = new Producao(false, "id");
		p3 = new Producao(false, ":");
		p4 = new Producao(true, "TIPO");
		p5 = new Producao(false, ".new");
		g2.adiciona(p0);
                g2.adiciona(p1);
		g2.adiciona(p2);
		g2.adiciona(p3);
		g2.adiciona(p4);
		g2.adiciona(p5);
		NT_DECL.adiciona(g2);
		
		//SD
		NT_SD = new NTerminais("SD");
		Grammar g = new Grammar("@"); 
		p1 = new Producao(true, "S");
		g.adiciona(p1);
		NT_SD.adiciona(g);
                g = new Grammar("\n"); 
                p0 = new Producao(true, "\n");
		p1 = new Producao(true, "S");
		g.adiciona(p0);
                g.adiciona(p1);
		NT_SD.adiciona(g);
                g = new Grammar("=");
                p1 = new Producao(false,"=");
                p2 = new Producao(true,"SD1");
		g.adiciona(p1);
                g.adiciona(p2);
                NT_SD.adiciona(g);
                
		
		//SD1
		NT_SD1 = new NTerminais("SD1");
		g = new Grammar("@"); 
		p1 = new Producao(false, "@");
		p2 = new Producao(false, "id");
		g.adiciona(p1);
		g.adiciona(p2);
		NT_SD1.adiciona(g);
		
		g = new Grammar("literal"); 
		p1 = new Producao(false, "literal");		
		g.adiciona(p1);
		NT_SD1.adiciona(g);
		
		//SA
				NT_SA = new NTerminais("SA");
				g = new Grammar("@"); 
				p1 = new Producao(true, "ATR");
				p2 = new Producao(true, "SA1");
				g.adiciona(p1);
				g.adiciona(p2);
				NT_SA.adiciona(g);
                                
                                g = new Grammar("\n");
                                p0 = new Producao (false,"\n");
				p1 = new Producao(true, "ATR");
				p2 = new Producao(true, "SA1");
				g.adiciona(p0);
                                g.adiciona(p1);
				g.adiciona(p2);
				NT_SA.adiciona(g);


				//ATR
				NT_ATR = new NTerminais("ATR");
				g = new Grammar("@"); 
				p1 = new Producao(false, "@");
				p2 = new Producao(false, "id");
				p3 = new Producao(false, "=");
				p4 = new Producao(true, "ATR1");
                                g.adiciona(p1);
				g.adiciona(p2);
                                g.adiciona(p3);
				g.adiciona(p4);
                                
				NT_ATR.adiciona(g);

				g = new Grammar("\n");
                                p0 = new Producao (false,"\n");
				p1 = new Producao(false, "@");
				p2 = new Producao(false, "id");
				p3 = new Producao(false, "=");
				p4 = new Producao(true, "ATR1");
                                g.adiciona(p0);
				g.adiciona(p1);
				g.adiciona(p2);
                                g.adiciona(p3);
                                g.adiciona(p4);
                                
				NT_ATR.adiciona(g);

				//ATR1
				NT_ATR1 = new NTerminais("ATR1");
				g = new Grammar("@"); 
				p1 = new Producao(true, "EXP");
				g.adiciona(p1);
				NT_ATR1.adiciona(g);

				g = new Grammar("literal"); 
				p1 = new Producao(false, "literal");
				g.adiciona(p1);
				NT_ATR1.adiciona(g);
				
				//EXP
				NT_EXP = new NTerminais("EXP");
				g = new Grammar("@");				
				p1 = new Producao(true, "OP");
				p2 = new Producao(true, "EXP1");
				g.adiciona(p1);
				g.adiciona(p2);
				NT_EXP.adiciona(g);

				g = new Grammar("(");				
				p1 = new Producao(false, "(");
				p2 = new Producao(true, "OP");
				p3 = new Producao(true, "EXP1");
				p4 = new Producao(false, ")");				
				g.adiciona(p1);
				g.adiciona(p2);
				g.adiciona(p3);
				g.adiciona(p4);				
				NT_EXP.adiciona(g);
				
				//EXP1
				NT_EXP1 = new NTerminais("EXP1");
				g = new Grammar("And");				
				p1 = new Producao(false, "And");
				p2 = new Producao(true, "EXP");
				g.adiciona(p1);
				g.adiciona(p2);
				NT_EXP1.adiciona(g);

				g = new Grammar("Or");				
				p1 = new Producao(false, "Or");
				p2 = new Producao(true, "EXP");
				g.adiciona(p1);
				g.adiciona(p2);
				NT_EXP1.adiciona(g);

				g = new Grammar("Not");				
				p1 = new Producao(false, "Not");
				p2 = new Producao(true, "EXP");
				g.adiciona(p1);
				g.adiciona(p2);
				NT_EXP1.adiciona(g);
				
				//OP
				NT_OP = new NTerminais("OP");
				g = new Grammar("@");				
				p1 = new Producao(false, "@");
				p2 = new Producao(false, "id");
				p3 = new Producao(true, "OP1");
				g.adiciona(p1);
				g.adiciona(p2);
				g.adiciona(p3);				
				NT_OP.adiciona(g);

				NT_OP1 = new NTerminais("OP1");				
				g = new Grammar("@");				
				p1 = new Producao(false, "@");
                                p2 = new Producao(false, "id");
				g.adiciona(p1);
				g.adiciona(p2);
                                
                                g2 = new Grammar("-");
                                p1 = new Producao (false,"-");
                                p2 = new Producao (true,"J");
                                g2.adiciona(p1);
                                g2.adiciona(p2);
                                
                               Grammar g3 = new Grammar("*");
                                p1 = new Producao (false,"*");
                                p2 = new Producao (true,"J");
                                g3.adiciona(p1);
                                g3.adiciona(p2);
                                
                              Grammar  g4 = new Grammar("/");
                                p1 = new Producao (false,"/");
                                p2 = new Producao (true,"J");
                                g4.adiciona(p1);
                                g4.adiciona(p2);
                                
                                Grammar g5 = new Grammar(">");
                                 p1 = new Producao (false,">");
                                 p2 = new Producao (true,"J");
                                 g5.adiciona(p1);
                                 g5.adiciona(p2);
                                
                               Grammar g6 = new Grammar("<");
                                p1 = new Producao (false,"<");
                                p2 = new Producao (true,"J");
                                g6.adiciona(p1);
                                g6.adiciona(p2);
                                
                                Grammar g7 = new Grammar(">=");
                                 p1 = new Producao (false,">=");
                                 p2 = new Producao (true,"J");
                                 g7.adiciona(p1);
                                 g7.adiciona(p2);
                                 
                                Grammar g8 = new Grammar("<=");
                                 p1 = new Producao (false,"<=");
                                 p2 = new Producao (true,"J");
                                 g8.adiciona(p1);
                                 g8.adiciona(p2);
                                 
                                Grammar g9 = new Grammar("==");
                                 p1 = new Producao (false,"==");
                                 p2 = new Producao (true,"J");
                                 g9.adiciona(p1);
                                 g9.adiciona(p2);
                                 
                                Grammar g10 = new Grammar("!=");
                                 p1 = new Producao (false,"!=");
                                 p2 = new Producao (true,"J");
                                 g10.adiciona(p1);
                                 g10.adiciona(p2);
                                 
                                 Grammar g11 = new Grammar("+");
                                  p1 = new Producao (false,"+");
                                  p2 = new Producao (true,"J");
                                  g11.adiciona(p1);
                                  g11.adiciona(p2);
                            
								NT_OP1.adiciona(g);
                                NT_OP1.adiciona(g1);
                                NT_OP1.adiciona(g2);
                                NT_OP1.adiciona(g3);
                                NT_OP1.adiciona(g4);
                                NT_OP1.adiciona(g5);
                                NT_OP1.adiciona(g6);
                                NT_OP1.adiciona(g7);
                                NT_OP1.adiciona(g8);
                                NT_OP1.adiciona(g9);
                                NT_OP1.adiciona(g10);
                                NT_OP1.adiciona(g11);
                                
                      //SA1
                                NT_SA1 = new NTerminais("SA1");
                                g1 = new Grammar (",");
                                p1 = new Producao(false,",");
                                p2 = new Producao(true,"SA");
                                g1.adiciona(p1);
                                g1.adiciona(p2);
                                NT_SA1.adiciona(g1);
                                
	}
	
	public void showTokens()
	{
		System.out.println("");
		System.out.println("________________________Lista de tokens_________________________-");
		
		for (Token token : listaTokens) {
			System.out.println("<" + String.valueOf(token.getTag()) + "," + token.getValue() + ">");
		}	
	}
	
	public void goParser()
	{
		//para cada token, verificar na tabela se ??? terminal ou n???o-terminal. Ap???s isso empilhar.
                int linha = 0;
				Producao aux; // auxiliares de verifica��o
                Object aux2;  //   --                   --
                NTerminais aux3;//------------------------
                EmpilhaProducoes("@", this.NT_S);
                aux2 = pilha.pop();   //  Casting para pegar a produ��o do topo( a pilha s� trabalhar� om produ��es
                aux  = (Producao) aux2;
		
				for (Token token : listaTokens) {
					int i = 0;
					if (token.getTag() == Tag.FIM)
					{
						linha++;
					}
					//Comeca com declaracao
                       while(aux.getFlag()){ // Enquanto for n�o terminal.
                           aux3 = getNT(aux.getValor()); // Pegue o N�o terminal do topo. Manda como par�metro o valor da produ��o.
                           
                           if (pilha.size() == 0){
                        	   break;
                           }
                                                     
                           if (token.getValue().charAt(0) == '@'){
                               this.EmpilhaProducoes("@", aux3);
                           }
                           else{
                               if ((token.getTag() == Tag.LITERALNUMERICO) || (token.getTag() == Tag.LITERALSTRING)){
                            	   this.EmpilhaProducoes("literal", aux3);
                               }
                               else if (aux3 != null){                            	   
                            	   this.EmpilhaProducoes(token.getValue(), aux3);
                               }
                            // Empilha as produ��es de tr�s para frente
                           }
                           
                           aux2 = pilha.pop(); //pega o primeiro objeto, e refaz o casting.
                       
                           if (aux2 != null)
                           {
                        	   aux  = (Producao) aux2;
                           }
                           else
                           {
                        	   break;
                           }
                      }
                       
                       
                       if(token.getValue().equals(aux.getValor()))
                       {
                           
                           aux2 = pilha.pop();   //  Casting para pegar a produ��o do topo( a pilha s� trabalhar� om produ��es
                           aux  = (Producao) aux2;
                       }
                       else{
                            if ((aux.getValor().equals(":")) && (token.getValue().equals("="))){// caso especial para pular para atribui��o.
                                this.EmpilhaProducoes("@", NT_SA);
                                 aux2 = pilha.pop();   //  Casting para pegar a produ��o do topo( a pilha s� trabalhar� om produ��es
                                 aux  = (Producao) aux2;
                            }// Depois disso tem que retornar a seta de leitura para o come�o.
                            else{//aqui é pra eliminar literais e id's.
                                   if ((token.getTag() == Tag.VARIAVEL) && (aux.getValor().equals("@"))){
                                        pilha.pop();
                                        aux2 = pilha.pop();   //  Casting para pegar a produ��o do topo( a pilha s� trabalhar� om produ��es
                                        aux  = (Producao) aux2;
                                   }
                                   else{
                                         if (aux.getValor().equals("literal")){
                                             if ((token.getTag() == Tag.LITERALNUMERICO) || (token.getTag() == Tag.LITERALSTRING)){
                                                
                                                 aux2 = pilha.pop();   //  Casting para pegar a produ��o do topo( a pilha s� trabalhar� om produ��es
                                                 aux  = (Producao) aux2;
                                             }
                                              else{
                                                    if ((aux.getValor() != "SD") && (aux.getValor() != "DECL") && (aux.getValor() != "SA1") && (aux.getValor() != "ATR") && (aux.getValor() != "EXPR1") && (aux.getValor() != "OP1")) 
                                                        System.out.println("Erro Linha" + String.valueOf(linha));//erro
                                                 }
                                         }
                                        
                                       }
                               }
                        }
                       
        }
	}
        
         private NTerminais getNT(String palavra){
            if (palavra.equals(this.J.getnome())){
                 return this.J;
             }
             if (palavra.equals(this.TIPO.getnome())){
                 return this.TIPO;
             }
             if (palavra.equals(this.NT_ATR.getnome())){
                 return this.NT_ATR;
             }
             if (palavra.equals(this.NT_ATR1.getnome())){
                 return this.NT_ATR1;
             }
             if (palavra.equals(this.NT_DECL.getnome())){
                 return this.NT_DECL;
             }
              if (palavra.equals(this.NT_EXP.getnome())){
                 return this.NT_EXP;
             }
               if (palavra.equals(this.NT_EXP1.getnome())){
                 return this.NT_EXP1;
             }
                if (palavra.equals(this.NT_OP.getnome())){
                 return this.NT_OP;
             }
                if (palavra.equals(this.NT_OP1.getnome())){
                 return this.NT_OP1;
             }
                if (palavra.equals(this.NT_S.getnome())){
                 return this.NT_S;
             }
                if (palavra.equals(this.NT_SA.getnome())){
                 return this.NT_SA;
             }
                if (palavra.equals(this.NT_SA1.getnome())){
                 return this.NT_SA1;
             }
                  if (palavra.equals(this.NT_SD.getnome())){
                 return this.NT_SD;
             }
                if (palavra.equals(this.NT_SD1.getnome())){
                 return this.NT_SD1;
             }               
              return NT_SD2;     
        }// Vai devlver o N�o-terminal . 
         
         private void EmpilhaProducoes(String chave, NTerminais NT){// Vai pegar com a Chave a lista Grammar correta.
             int i;
             Object aux1;
             Grammar aux2;
             aux1 = NT.Glista(chave);
             aux2 = (Grammar) aux1;
             
             if ((aux1 == null) || aux2 == null )
             {
            	 //erro
            	 return;
             }
             
             for (i = aux2.getProdlist().size() - 1;i > -1; i--){ //empilha de tr�s pra frente
                 this.pilha.push(aux2.getProdlist(i));
             }
         } // Empilh
        
}
